package scalaSci;


class Matr(els: List[List[Double]]) {

     /** elements of the matrix, stored as a list of  its rows */
      val elements: List[List[Double]] = els

       def nRows: Int = elements.length
       def nCols: Int = if (elements.isEmpty) 0 else elements.head.length
   
   /** all rows of the matrix must have the same  number of columns */
    require(elements.forall(_.length == nCols))

private def addRows(a: List[Double],   b: List[Double]):
    List[Double] = List.map2(a,b)(_+_)

  private def subRows(a: List[Double],  b: List[Double]):
    List[Double] =  List.map2(a,b)(_-_)

  def +(other: Matr): Matr = {
    require((other.nRows == nRows) &&
            (other.nCols == nCols))
    new Matr(List.map2(elements, other.elements)
        (addRows(_,_))
    )
  }

  def -(other: Matr): Matr = {
    require((other.nRows == nRows) &&
            (other.nCols == nCols))
    new Matr(
      List.map2(elements, other.elements)
        (subRows(_,_))
    ) 
  }

  def transpose(): Matr =
    new Matr(List.transpose(elements))

       private def dotVectors(a: List[Double],b: List[Double]):  Double = {
  val multipliedElements =    List.map2(a,b)(_*_)
  (0.0 /: multipliedElements)(_+_)
   }

  def *(other: Matr): Matr = {
  require(nCols == other.nRows)
  val t = other.transpose()
  new Matr(
       for (row <- elements) yield {
         for (otherCol <- t.elements)
         yield dotVectors(row, otherCol)
       }
    )
 }

 override def toString(): String = {
     val rowStrings =
       for (row <- elements)
         yield row.mkString("[", ", ", "]")
       rowStrings.mkString("", "\n", "\n")
  }
       }

   
// val x = Matr(3, 1,2,3,4,5,6)
// x*x.transpose 

